package com.xzcs.shiro;

import com.xzcs.mapper.user.UserMapper;
import com.xzcs.util.common.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

/**
 * 自定义realm类，该类需要继承AuthorizingRealm，并重写认证、授权方法
 * @desc:
 * @author: chang
 * @date: 2017/6/7
 */
public class MyAuthorizingRealm extends AuthorizingRealm{

	public Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private UserMapper userMapper;

	/**
	 * 只有需要验证权限时才会调用, 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.在配有缓存的情况下，只加载一次.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		//String userName = (String) principalCollection.fromRealm(getName()).iterator().next();
		String userName = SecurityUtils.getSubject().getPrincipal().toString();
		if (null != userName) {
			Map<String, Object> userInfo = userMapper.findByUserName(userName);
			String userId = StringUtils.get(userInfo, "user_id");
			List<Map<String, Object>> permits = userMapper.findUserPermit(userId);
			List<Map<String, Object>> userRole = userMapper.findUserRole(userId);
			//权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
			SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
			for (Map<String, Object> map : userRole) {
				String roleId = StringUtils.get(map, "role_id");
				authorInfo.addRole(roleId);
			}
			for (Map<String, Object> map : permits) {
				String permitCode = StringUtils.get(map, "permit_code");
				authorInfo.addStringPermission(permitCode);
			}
			return authorInfo;
		}
		return null;
	}

	/**
	 * 认证回调函数,登录时调用
	 * 首先根据传入的用户名获取User信息；然后如果user为空，那么抛出没找到帐号异常UnknownAccountException；
	 * 如果user找到但锁定了抛出锁定异常LockedAccountException；最后生成AuthenticationInfo信息，
	 * 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配，
	 * 如果不匹配将抛出密码错误异常IncorrectCredentialsException；
	 * 另外如果密码重试此处太多将抛出超出重试次数异常ExcessiveAttemptsException；
	 * 在组装SimpleAuthenticationInfo信息时， 需要传入：身份信息（用户名）、凭据（密文密码）、盐（username+salt），
	 * CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		String userName = (String) authcToken.getPrincipal();
		Map<String, Object> userInfo = userMapper.findByUserName(userName);
		if (null != userInfo) {
			if (!"1".equals(StringUtils.get(userInfo, "status"))) {
				throw new LockedAccountException();
			}
			SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName,
					StringUtils.get(userInfo, "user_pwd"),
					getName());
			Session session = SecurityUtils.getSubject().getSession();
			session.setAttribute("user", userInfo);
			return authenticationInfo;
		} else {
			throw new UnknownAccountException();//未找到账号
		}
	}
}